using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using WozLib.UI.Ink.SketchProperties;
using WozLib.UI.Util;

namespace WozLib.UI.Controls
{
  public class SelectableColor
  {
    private static List<Color> _RegisteredColors = new List<Color>();

    public Color Color
    {
      get { return _Color; }
      set { _Color = value; }
    } private Color _Color;

    public string Description
    {
      get { return _Description; }
      set { _Description = value; }
    } private string _Description;

    private bool _IsOther;
      
    public bool IsOther
    {
      get { return _IsOther; }
      set { _IsOther = value; }
    }

    public SelectableColor(bool isOther)
    {
      IsOther = isOther;

      Color = Colors.Orange;
      Description = "Other";
    }

    public SelectableColor(string description, Color color)
    {
      IsOther = false;
      Color = color;
      _RegisteredColors.Add(color);
      Description = description;
    }

    public override string ToString()
    {
      return Description;
    }

    // broken
    public override bool Equals(object obj)
    {
      if (obj is Color)
      {
        if (IsOther && !_RegisteredColors.Contains((Color)obj))
        {
          Color = (Color)obj;
          return true;
        }
        else if (!IsOther && ((Color)obj).Equals(Color))
        {
          return true;
        }
        else
        {
          return false;
        }
      }
      else if (obj is SelectableColor)
      {
        return Equals((obj as SelectableColor).Color);
      }
      else
      {
        return false;
      }
    }
  }

  /// <summary>
  /// Interaction logic for ColorSelector.xaml
  /// </summary>
  public partial class ColorSelector : UserControl
  {
    // Using a DependencyProperty as the backing store for SelectedColor. This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SelectedColorProperty =
            DependencyProperty.Register("SelectedColor", typeof(Color), typeof(ColorSelector),
      new UIPropertyMetadata(HlsConverter.ConvertFromHls(0, 0.5, 1), new PropertyChangedCallback(_OnColorChanged)));

    public static readonly DependencyProperty ColorCascadingPropertyProperty =
            DependencyProperty.Register("ColorCascading", typeof(ColorCascadingProperty), typeof(ColorSelector));

    public static readonly DependencyProperty SelectedColorAProperty =
            DependencyProperty.Register("SelectedColorA", typeof(Byte), typeof(ColorSelector),
      new UIPropertyMetadata(new PropertyChangedCallback(SelectedColorComponent_Changed)));

    public static readonly DependencyProperty SelectedColorScAProperty =
            DependencyProperty.Register("SelectedColorScA", typeof(double), typeof(ColorSelector),
      new UIPropertyMetadata(new PropertyChangedCallback(SelectedColorComponent_Changed)));

    public static readonly DependencyProperty SelectedColorRProperty =
            DependencyProperty.Register("SelectedColorR", typeof(Byte), typeof(ColorSelector),
      new UIPropertyMetadata(new PropertyChangedCallback(SelectedColorComponent_Changed)));

    public static readonly DependencyProperty SelectedColorGProperty =
            DependencyProperty.Register("SelectedColorG", typeof(Byte), typeof(ColorSelector),
      new UIPropertyMetadata(new PropertyChangedCallback(SelectedColorComponent_Changed)));

    public static readonly DependencyProperty SelectedColorBProperty =
            DependencyProperty.Register("SelectedColorB", typeof(Byte), typeof(ColorSelector),
      new UIPropertyMetadata(new PropertyChangedCallback(SelectedColorComponent_Changed)));

    // Using a DependencyProperty as the backing store for HlsValue. This enables animation, styling, binding, etc...
    public static readonly DependencyProperty HlsValueProperty =
            DependencyProperty.Register("HlsValue", typeof(HlsValue), typeof(ColorSelector),
      new UIPropertyMetadata(new HlsValue(0, 0.5, 1), new PropertyChangedCallback(_OnHlsValueChanged)));

    public ColorSelector()
    {
      InitializeComponent();

      this.Loaded += new RoutedEventHandler(ColorSelector_Loaded);
    }

    void ColorSelector_Loaded(object sender, RoutedEventArgs e)
    {
      colSlider.ValueChanged += new RoutedPropertyChangedEventHandler<double>(colSlider_ValueChanged);
      mousePath.MouseDown += new MouseButtonEventHandler(mousePath_MouseDown);
      mousePath.MouseUp += new MouseButtonEventHandler(mousePath_MouseUp);
      mousePath.MouseMove += new MouseEventHandler(mousePath_MouseMove);

      mousePath.PreviewMouseDown += new MouseButtonEventHandler(mousePath_PreviewMouseDown);
      mousePath.PreviewMouseUp += new MouseButtonEventHandler(mousePath_PreviewMouseUp);
      alphaSlider.PreviewMouseDown += new MouseButtonEventHandler(alphaSlider_PreviewMouseDown);
      alphaSlider.PreviewMouseUp += new MouseButtonEventHandler(alphaSlider_PreviewMouseUp);
      colSlider.PreviewMouseDown += new MouseButtonEventHandler(colSlider_PreviewMouseDown);
      colSlider.PreviewMouseUp += new MouseButtonEventHandler(colSlider_PreviewMouseUp);

      //BaseColor="{Binding ElementName=colSelector, Path=SelectedColor}"
      Binding basecolorBinding = new Binding();
      basecolorBinding.Mode = BindingMode.OneWay;
      basecolorBinding.Source = this;
      basecolorBinding.Path = new PropertyPath(SelectedColorProperty);
      BindingOperations.SetBinding(alphaSlider, AlphaSlider.BaseColorProperty, basecolorBinding);

      Binding valueBinding = new Binding();
      valueBinding.Mode = BindingMode.TwoWay;
      valueBinding.Source = this;
      valueBinding.Path = new PropertyPath(SelectedColorScAProperty);
      BindingOperations.SetBinding(alphaSlider, AlphaSlider.ValueProperty, valueBinding);
      
      //OnHlsValueChanged(HlsValue);

      _InformalColorCombo.Items.Add(new SelectableColor("Black",Colors.Black));
      _InformalColorCombo.Items.Add(new SelectableColor("Blue", Colors.Blue));
      _InformalColorCombo.Items.Add(new SelectableColor("Red", Colors.Red));
      _InformalColorCombo.Items.Add(new SelectableColor("Green", Colors.Green));
      _InformalColorCombo.Items.Add(new SelectableColor("White", Colors.White));
      _InformalColorCombo.Items.Add(new SelectableColor(true));

      updateInformalColorCombo();

      _InformalColorCombo.SelectionChanged += new SelectionChangedEventHandler(_InformalColorCombo_SelectionChanged);

      if (ColorCascading != null && ColorCascading.WozPropertyVariants != null && ColorCascading.WozPropertyVariants.Count > 1)
      {
        foreach (SketchProperty wozProperty in ColorCascading.WozPropertyVariants)
        {
          if (wozProperty is ColorSketchProperty)
          {
            Rectangle r = new Rectangle();
            r.Width = 15.0;
            r.Height = 15.0;
            r.RadiusX = 3.0;
            r.RadiusY = 3.0;
            r.Margin = new Thickness(3.0);
            r.Fill = new SolidColorBrush((wozProperty as ColorSketchProperty).Data);
            _ColorOptionStack.Children.Add(r);
            r.MouseDown += new MouseButtonEventHandler(r_MouseDown);
          }
        }
      }
    }

    void r_MouseDown(object sender, MouseButtonEventArgs e)
    {
      if (sender is Shape && (sender as Shape).Fill is SolidColorBrush)
      {
        this.SelectedColor = ((sender as Shape).Fill as SolidColorBrush).Color;
      }
      // so we don't set off the header
      e.Handled = true;
    }

    void mousePath_PreviewMouseUp(object sender, MouseButtonEventArgs e)
    {
      MouseUpMaster();
    }

    void mousePath_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
      MouseDownMaster();
    }

    void colSlider_PreviewMouseUp(object sender, MouseButtonEventArgs e)
    {
      MouseUpMaster();
    }

    void colSlider_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
      MouseDownMaster();
    }

    void alphaSlider_PreviewMouseUp(object sender, MouseButtonEventArgs e)
    {
      MouseUpMaster();
    }

    void alphaSlider_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
      MouseDownMaster();
    }

    void _InformalColorCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
      if (_InformalColorCombo.SelectedItem is SelectableColor)
      {
        SelectedColor = (_InformalColorCombo.SelectedItem as SelectableColor).Color;
      }
    }

    private void updateInformalColorCombo()
    {
      foreach (SelectableColor sc in _InformalColorCombo.Items)
      {
        if (sc.Equals(SelectedColor))
        {
          _InformalColorCombo.SelectedItem = sc;
        }
      }
    }

    void mousePath_MouseMove(object sender, MouseEventArgs e)
    {
      if (Mouse.LeftButton == MouseButtonState.Pressed)
      {
        UpdateFromMousePosition(Mouse.GetPosition(mousePath));
      }
    }

    void mousePath_MouseUp(object sender, MouseButtonEventArgs e)
    {
      Mouse.Capture(mousePath, CaptureMode.None);
      
    }

    private void MouseUpMaster()
    {
      ColorCascading.InContinuousChange = false;
    }

    void mousePath_MouseDown(object sender, MouseButtonEventArgs e)
    {
      
      Mouse.Capture(mousePath, CaptureMode.Element);
      UpdateFromMousePosition(Mouse.GetPosition(mousePath));
    }

    private void MouseDownMaster()
    {
      ColorCascading.InContinuousChange = true;
    }

    void colSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
      HlsValue = new HlsValue(e.NewValue, HlsValue.Lightness, HlsValue.Saturation);
    }

    private void UpdateFromMousePosition(System.Windows.Point position)
    {
      if (position.X < 0) position.X = 0;
      if (position.X > 100) position.X = 100;

      if (mousePath.ActualHeight - (100 - 0.5 * position.X) > position.Y) position.Y = mousePath.ActualHeight - (100 - 0.5 * position.X);
      if (mousePath.ActualHeight - (0.5 * position.X) < position.Y) position.Y = mousePath.ActualHeight - (0.5 * position.X);

      if (position.Y < 0) position.Y = 0;
      if (position.Y > 100) position.Y = 100;

      // SelectedColor = HlsConverter.ConvertFromHls(Me.colSlider.Value, 1 - pos.Y / mousePath.ActualHeight, pos.X / mousePath.ActualWidth)
      HlsValue = new HlsValue(this.colSlider.Value, 1 - position.Y / mousePath.ActualHeight, position.X / mousePath.ActualWidth);
    }

    private bool suspendSet = false;
    public Color SelectedColor
    {
      get
      {
        return (Color)GetValue(SelectedColorProperty);
      }
      set
      {
        if (!suspendSet)
        {
          suspendSet = true;
          SetValue(SelectedColorProperty, value);
          suspendSet = false;
        }
      }
    }

    public ColorCascadingProperty ColorCascading
    {
      get
      {
        return (ColorCascadingProperty)GetValue(ColorCascadingPropertyProperty);
      }
      set
      {
        SetValue(ColorCascadingPropertyProperty, value);
      }
    }
    

    private static void SelectedColorComponent_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {

        if (d is ColorSelector)
        {
          ColorSelector colorSelector = d as ColorSelector;
          if (!colorSelector.suspendSet)
          {
            //colorSelector.suspendSet = true;
            if (e.Property == SelectedColorAProperty)
            {
              Color c = colorSelector.SelectedColor;
              colorSelector.SelectedColor = Color.FromArgb((byte)e.NewValue, c.R, c.G, c.B);
            }
            else if (e.Property == SelectedColorScAProperty)
            {
              Color c = colorSelector.SelectedColor;
              colorSelector.SelectedColor = Color.FromArgb((byte)(((double)e.NewValue) * 255), c.R, c.G, c.B);
            }
            else if (e.Property == SelectedColorRProperty)
            {
              Color c = colorSelector.SelectedColor;
              colorSelector.SelectedColor = Color.FromArgb(c.A, (byte)e.NewValue, c.G, c.B);
            }
            else if (e.Property == SelectedColorGProperty)
            {
              Color c = colorSelector.SelectedColor;
              colorSelector.SelectedColor = Color.FromArgb(c.A, c.R, (byte)e.NewValue, c.B);
            }
            else if (e.Property == SelectedColorBProperty)
            {
              Color c = colorSelector.SelectedColor;
              colorSelector.SelectedColor = Color.FromArgb(c.A, c.R, c.G, (byte)e.NewValue);
            }
            //colorSelector.suspendSet = false;
          }
        }
    }

    public HlsValue HlsValue
    {
      get
      {
        return (HlsValue)GetValue(HlsValueProperty);
      }
      set
      {
        SetValue(HlsValueProperty, value);
      }
    }

    private static void _OnColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      Color value = (Color)e.NewValue;
      // don't update the color, because an updated color is what is causeing the HLS value to be updated
      (d as ColorSelector).OnHlsValueChanged(HlsConverter.ConvertToHls(value),false);
      d.SetValue(SelectedColorAProperty, value.A);
      d.SetValue(SelectedColorScAProperty, (double)value.ScA);
      d.SetValue(SelectedColorRProperty, value.R);
      d.SetValue(SelectedColorGProperty, value.G);
      d.SetValue(SelectedColorBProperty, value.B);
      (d as ColorSelector).updateInformalColorCombo();
    }

    private static void _OnHlsValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      HlsValue oldHls;
      HlsValue newHls;

      oldHls = (HlsValue)e.OldValue;
      newHls = (HlsValue)e.NewValue;
      if (oldHls != newHls)
      {
        //Console.WriteLine("{0} {1}", oldHls, newHls);

        (d as ColorSelector).OnHlsValueChanged((HlsValue)e.NewValue);
      }
    }

    bool suspendHlsValueChange = false;
    private void OnHlsValueChanged(HlsValue value)
    {
      OnHlsValueChanged(value, true);
    }

    private void OnHlsValueChanged(HlsValue value, bool updateColor)
    {
      if (!suspendHlsValueChange)
      {
        suspendHlsValueChange = true;

        if (updateColor)
        {
          Color newColor = HlsConverter.ConvertFromHls(value);

          if (newColor != this.SelectedColor) SelectedColor = newColor;
        }

        this.UpdateEllipse(value.Lightness, value.Saturation);
        this.UpdatePath(value.Hue);
        this.UpdateSlider(value.Hue);

        suspendHlsValueChange = false;
      }
    }

    private void UpdateEllipse(double l, double s)
    {
      Canvas.SetTop(this.ellSelecttion, (1 - l) * mousePath.ActualHeight - 5);
      Canvas.SetLeft(this.ellSelecttion, s * mousePath.ActualWidth - 5);
    }

    private void UpdateSlider(double hue)
    {
      double delta;

      delta = Math.Abs(colSlider.Value - hue);

      if (delta >= 0.01) colSlider.Value = hue;
    }

    private void UpdatePath(double hue)
    {
      coloredPath.Fill = new SolidColorBrush(HlsConverter.ConvertFromHls(hue, 0.5, 1));
    }
  }
}